#include <QJsonObject>
#include <QJsonArray>

#include <osg/Geometry>
#include <osgUtil/SmoothingVisitor>
#include <osg/BlendFunc>
#include <GL/GLU.h>

#include "Triangles.h"
#include "JsonReader.h"
#include "GLSLPrograms.h"
#include "OsgUtil.h"

#pragma comment(lib, "Glu32.lib")
// 仅适用于section是凸多边形
void getConvexPolygonTrianglesData(const std::vector<osg::Vec3d>& section, std::vector<osg::Vec3d>& points);
static float s_alpha = 1.0f;

void setTrianglesNormals(osg::Geometry& geoset)
{
	const auto& psl = geoset.getPrimitiveSetList();
	if (psl.size() != 1)
		return;
	auto mode = psl[0]->getMode();
	switch (mode)
	{
	case osg::PrimitiveSet::TRIANGLES:
	{
		auto pva = dynamic_cast<osg::Vec3Array*>(geoset.getVertexArray());
		if (!pva)
			return;
		osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array(osg::Array::BIND_PER_VERTEX);
		auto& va = *pva;
		for (int i = 0; i < va.size(); i += 3)
		{
			auto v1 = va[i + 1] - va[i];
			auto v2 = va[i + 2] - va[i + 1];
			auto v = v1 ^ v2;
			v.normalize();
			normals->push_back(v);
			normals->push_back(v);
			normals->push_back(v);
		}
		geoset.setNormalArray(normals);
	}
	break;
	case osg::PrimitiveSet::TRIANGLE_STRIP:
	case osg::PrimitiveSet::TRIANGLE_FAN:
		osgUtil::SmoothingVisitor::smooth(geoset);//自动生成法线
		break;
	default:
		break;
	}
}

// 仅适用于section是凸多边形
void getConvexPolygonTrianglesData(const std::vector<osg::Vec3d>& section, std::vector<osg::Vec3d>& points)
{
	int sectionPointCount = section.size();
	// 计算中心点
	osg::Vec3d centerPoint;
	for (int i = 0; i < sectionPointCount; ++i)
	{
		centerPoint += section[i];
	}
	centerPoint /= sectionPointCount;
	for (int i = 0; i < sectionPointCount; i++)
	{
		int ip1 = (i + 1) % sectionPointCount;
		points.push_back(centerPoint);
		points.push_back(section[i]);
		points.push_back(section[ip1]);
	}
}

//剔除共线点
void removeCollinearPoint(std::vector<osg::Vec3d>& points, double detError = 0.01)
{
	if (points.size() < 2)
		return;
	std::vector<int> indexes;
	for (int i = 0; i < int(points.size() - 1); i++)
	{
		osg::Vec3d v1(points[i] - points[i + 1]);
		double len = v1.length();
		if (len < 1.0E-5)
		{
			indexes.push_back(i);
		}
	}
	for (auto it = indexes.rbegin(); it != indexes.rend(); ++it)
	{
		points.erase(points.begin() + *it);
	}

	indexes.clear();
	for (int i = 1; i < points.size() - 1; i++)
	{
		osg::Vec3d v1(points[i + 1] - points[i - 1]);
		double lineLen2 = v1.length2();
		osg::Vec3d v2(points[i] - points[i - 1]);
		double area = (v1 ^ v2).length();
		if (area / lineLen2 <= detError) // area / lineLen2 = 点到线段的距离/线段长度
		{
			v1.normalize();
			v2.normalize();
			if (std::abs(v1 * v2) > 0.9998476951563913/*cos(1deg)*/)
				indexes.push_back(i);
		}
	}
	for (auto it = indexes.rbegin(); it != indexes.rend(); ++it)
	{
		points.erase(points.begin() + *it);
	}
}

bool getConvexHull(const std::vector<osg::Vec3d> &points, std::vector<osg::Vec3d>& hullPoints);
namespace
{
	struct PolygonData
	{
		GLenum mode;
		std::vector<osg::Vec3d> tmpPoints;

		// 镶嵌需要double类型, section2d = section2
		std::vector<osg::Vec3d> section2d;
		const std::vector<osg::Vec3d>& section2;
		std::vector<osg::Vec3d>& points2;
		//冗余成员（当多边形有孔时使用）
		std::vector<std::vector<osg::Vec3d>> innerSection;

		osg::Vec3d normal;
		double normalLen = -1;

		PolygonData(const std::vector<osg::Vec3d>& section, std::vector<osg::Vec3d>& points)
			: section2(section)
			, points2(points)
		{
			for (const auto& pt : section)
			{
				section2d.push_back(pt);
			}
			calNormal();
		}
		PolygonData(const std::vector<osg::Vec3d>& section, const std::vector<std::vector<osg::Vec3d>>& innerSectionPoints, std::vector<osg::Vec3d>& points)
			: section2(section)
			, points2(points)
		{
			for (const auto& pt : section)
			{
				section2d.push_back(pt);
			}
			calNormal();
			for (auto section : innerSectionPoints)
			{
				std::vector<osg::Vec3d> tmp;
				for (auto pt : section)
				{
					tmp.push_back(pt);
				}
				innerSection.push_back(tmp);
			}
		}
		void calNormal()
		{
			std::vector<osg::Vec3d> hull;
			bool success = getConvexHull(section2, hull);
			if (success)
			{
				removeCollinearPoint(hull, 0.01);
				if (hull.size() >= 3)
				{
					normal = (hull[1] - hull[0]) ^ (hull[2] - hull[1]);
					normalLen = normal.normalize();
				}
			}
		}
	};
	void __stdcall myBeginData(GLenum mode, PolygonData* data)
	{
		data->mode = mode;
		data->tmpPoints.clear();
	}

	void __stdcall myVertexData(const GLdouble *v, PolygonData* data)
	{
		data->tmpPoints.push_back(osg::Vec3d(v[0], v[1], v[2]));
	}

	void __stdcall myEndData(PolygonData* data)
	{
		switch (data->mode)
		{
		case GL_TRIANGLES:
			for (int i = 0; i < data->tmpPoints.size(); ++i)
			{
				data->points2.push_back(data->tmpPoints[i]);
			}
			break;
			// 把下列模式翻译成GL_TRIANGLES
		case GL_TRIANGLE_STRIP:
			for (int i = 0; i < data->tmpPoints.size() - 2; ++i)
			{
				auto thisNormal = (data->tmpPoints[i + 1] - data->tmpPoints[i + 0]) ^ (data->tmpPoints[i + 2] - data->tmpPoints[i + 1]);
				thisNormal.normalize();
				if (data->normal * thisNormal > 0)
				{
					// 绕向相同
					for (int j = 0; j < 3; ++j)
					{
						data->points2.push_back(data->tmpPoints[i + j]);
					}
				}
				else
				{
					// 绕向相反
					data->points2.push_back(data->tmpPoints[i + 0]);
					data->points2.push_back(data->tmpPoints[i + 2]);
					data->points2.push_back(data->tmpPoints[i + 1]);
				}
			}
			break;
		case GL_TRIANGLE_FAN:
			for (int i = 0; i < data->tmpPoints.size() - 2; ++i)
			{
				data->points2.push_back(data->tmpPoints[0]);
				for (int j = 1; j <= 2; ++j)
				{
					data->points2.push_back(data->tmpPoints[i + j]);
				}
			}
			break;
		case GL_POLYGON:
			getConvexPolygonTrianglesData(data->section2, data->points2);
			break;
		}
	}
}

// section可以是凹多边形
void getSectionTrianglesData(const std::vector<osg::Vec3d>& loop, std::vector<osg::Vec3d>& points)
{
	int sectionPointCount = loop.size();
	if (sectionPointCount < 3)
		return;
	PolygonData data(loop, points);
	// 不考虑太小的多边形或者两条边几乎共线的多边形
	if (data.normalLen <= 0)
		return;
	GLUtesselator* tess = gluNewTess();
	typedef void (CALLBACK *fn)();
	gluTessCallback(tess, GLU_TESS_BEGIN_DATA, (fn)myBeginData);
	gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (fn)myVertexData);
	gluTessCallback(tess, GLU_TESS_END_DATA, (fn)myEndData);

	gluTessNormal(tess, data.normal.x(), data.normal.y(), data.normal.z());
	gluTessBeginPolygon(tess, &data);
	gluTessBeginContour(tess);
	for (int i = 0; i < sectionPointCount; ++i)
	{
		gluTessVertex(tess, data.section2d[i]._v, (void*)data.section2d[i]._v);
	}
	gluTessEndContour(tess);
	gluTessEndPolygon(tess);

	gluDeleteTess(tess);
}